home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / GLX / dials+buttons / dnbglx.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  32.5 KB  |  919 lines

  1. /*
  2.  * Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /*
  18.  * dnbglx.c :  a mixed model (GL & Xlib) dial-and-button box program.
  19.  *             imitates behavior of the Dial & Button Confidence Test 
  20.  *             accessible via the System Manager toolchest.
  21.  *
  22.  *   This program creates 2 mixed model GL windows placed side-by-side: 
  23.  *    1. a singlebuffer'd GLX window is used to draw the buttons and 
  24.  *       the text area (where animation is not necessary), 
  25.  *    2. a doublebuffer'd GLX window draws the dials (smooth animation 
  26.  *       *is* needed here).
  27.  *
  28.  *   There are 2 functions prior to the infinite loop that bear studying:
  29.  *
  30.  *    1. openwindow() sets up the X parent (including telling the WM what
  31.  *       attributes and constraints we'd like to have), the two GLX 
  32.  *       children, specifies what events each window is interested in,
  33.  *       loads the correct colormaps and then maps all three windows
  34.  *       (they will not become visible however until the first expose
  35.  *       events are generated/processed).
  36.  *
  37.  *    2. setupdevs() finds, opens, and creates a handle to the 
  38.  *       "dial+buttons" device structure.  it then determines the given 
  39.  *       type and class of each device event we're going to be interested 
  40.  *       in, and then makes requests to the server to send us events that 
  41.  *       match the events and devices described by the event list *and*
  42.  *       that come from our specific window.
  43.  *
  44.  *    NOTE:  the functionality of the GL routine SETVALUATOR(3G) (assign 
  45.  *           an initial value *and* range to a valuator) is not currently 
  46.  *           implemented in any X input extension from MIT.  Hence, for
  47.  *           the time being in this mixed-model imitation of the pure GL
  48.  *           dial and buttons confidence test, the dial valuator's are 
  49.  *           NOT constrained in their range to only [0..1023].  This is
  50.  *           the only known discrepancy between this program and the
  51.  *           original.  (There is a proposal to add this extension 
  52.  *           currently being debated but it is not finalized yet.)
  53.  *
  54.  *   Following this, the get/process input infinite loop occupies the
  55.  *    rest of the program's energies.  the core of this is the "default"
  56.  *    portion of the "switch (event.type)" statement which catches the
  57.  *    dial and button events being generated.  notice this is where the
  58.  *    {DnB_motion_type, DnB_press_type, DnB_release_type} vars come into 
  59.  *    play:  recall these were defined in setupdevs() with the 3 macros 
  60.  *    DeviceMotionNotify, DeviceButtonPress, and DeviceButtonRelease,
  61.  *    respectively.  XSelectExtensionEvent then was used to ask the
  62.  *    server to send us any events generated by these devices in our
  63.  *    window.
  64.  *
  65.  *                                   ratmandu -- november 1991
  66.  */
  67. #include <stdio.h>
  68. #include <stdlib.h>
  69. #include <X11/Xlib.h>
  70. #include <X11/Xutil.h>
  71. #include <X11/Xos.h>
  72. #include <X11/Xatom.h>
  73. #include <X11/extensions/XI.h>
  74. #include <X11/extensions/XInput.h>
  75. #include <gl/glws.h>
  76. #include <gl/gl.h>
  77.  
  78. /* specify window's "unresize-able" dimensions */
  79. #define XSIZE 840
  80. #define YSIZE 480
  81.  
  82. #define NumButtons 32
  83. #define NumDials    8
  84.  
  85. #define SWITCH_OFF  0
  86. #define SWITCH_ON   1
  87.  
  88. #define top     0                                   /* the X parent window */
  89. #define SBglwin 1       /* the "buttons" are drawn in a singlebuffered win */
  90. #define DBglwin 2       /* and the "dials" are drawn in a doublebuffer one */
  91. #define MAXWINS 3
  92.  
  93. typedef struct {         /* struct for each button's bottom-left origin    */
  94.     Coord xpos;          /* (i.e. position), and whether its being pressed */
  95.     Coord ypos;
  96.     int   state;
  97. } DB_BUTTON;
  98.  
  99. DB_BUTTON but[] = {               /* the button's initialized values array */
  100.     11.0, 31.0, SWITCH_OFF,
  101.     16.0, 31.0, SWITCH_OFF,
  102.     21.0, 31.0, SWITCH_OFF,
  103.     26.0, 31.0, SWITCH_OFF,
  104.     6.0,  26.0, SWITCH_OFF,
  105.     11.0, 26.0, SWITCH_OFF,
  106.     16.0, 26.0, SWITCH_OFF,
  107.     21.0, 26.0, SWITCH_OFF,
  108.     26.0, 26.0, SWITCH_OFF,
  109.     31.0, 26.0, SWITCH_OFF,
  110.     6.0,  21.0, SWITCH_OFF,
  111.     11.0, 21.0, SWITCH_OFF,
  112.     16.0, 21.0, SWITCH_OFF,
  113.     21.0, 21.0, SWITCH_OFF,
  114.     26.0, 21.0, SWITCH_OFF,
  115.     31.0, 21.0, SWITCH_OFF,
  116.     6.0,  16.0, SWITCH_OFF,
  117.     11.0, 16.0, SWITCH_OFF,
  118.     16.0, 16.0, SWITCH_OFF,
  119.     21.0, 16.0, SWITCH_OFF,
  120.     26.0, 16.0, SWITCH_OFF,
  121.     31.0, 16.0, SWITCH_OFF,
  122.     6.0,  11.0, SWITCH_OFF,
  123.     11.0, 11.0, SWITCH_OFF,
  124.     16.0, 11.0, SWITCH_OFF,
  125.     21.0, 11.0, SWITCH_OFF,
  126.     26.0, 11.0, SWITCH_OFF,
  127.     31.0, 11.0, SWITCH_OFF,
  128.     11.0, 6.0,  SWITCH_OFF,
  129.     16.0, 6.0,  SWITCH_OFF,
  130.     21.0, 6.0,  SWITCH_OFF,
  131.     26.0, 6.0,  SWITCH_OFF,
  132. };
  133.  
  134. typedef struct {                /* struct for each dial's center point and */
  135.     Coord xcntr;                /* current angle value (0 is 12 o'clock)   */
  136.     Coord ycntr;
  137.     int   angle;
  138. } DB_DIAL;
  139.  
  140. DB_DIAL dia[] = {                   /* the dial's initialized values array */
  141.     10.0, 8.0,  0,
  142.     20.0, 8.0,  0,
  143.     10.0, 15.0, 0,
  144.     20.0, 15.0, 0,
  145.     10.0, 22.0, 0,
  146.     20.0, 22.0, 0,
  147.     10.0, 29.0, 0,
  148.     20.0, 29.0, 0,
  149. };
  150.  
  151.       /* function declarations */
  152. static void openwindow(char *);
  153. static void setupdevs(int *, int *, int *);
  154. static Window glx_create_window(Window, int, int, int, int, int, char *);
  155. static void makeSBwin(), makeDBwin(), draw_dial(int), draw_button(int, int);
  156. static void clean_exit();
  157. static void Winset(Window, const int);
  158.  
  159.  
  160. static Display *dpy;                              /* X server connection   */
  161. static Atom del_atom;                             /* WM_DELETE_WINDOW atom */
  162. static Window wins[MAXWINS];       /* holds X parent & 2 GL window handles */
  163.  
  164.       /* dial and buttons device event handles */
  165. int DnB_device_id;                              /* device handles for the  */
  166. int DnB_press_type;                             /* DnB id, and the press,  */
  167. int DnB_release_type;                           /* release, & motion       */
  168. int DnB_motion_type;                            /* events they'll generate */
  169.  
  170. short firstdialtouched = FALSE;       /* used so text for dial valuators   */
  171.                                       /* doesn't get drawn until the dials */
  172.                                       /* actually are turned               */
  173.  
  174. int myExpose;                          /* needs to be global for makeDBwin */
  175. int dialaxis[NumDials];    /* tells which dial(s) is(are) currently active */
  176.  
  177.  
  178. main(int argc, char *argv[])
  179. {
  180.     int myConfigure, myMotion, 
  181.         myButtPress, myButtRelease;            /* track which events occur */
  182.     int buttP[NumButtons], buttR[NumButtons];  /* track which buttons are  */
  183.                            /* pressed or released      */
  184.     long i;
  185.     long windowvisible;       /* tells event handler whether or not window */
  186.                   /* is iconified--when it is, we don't want   */
  187.                               /* to process any incoming events            */
  188.  
  189.  
  190.  
  191.     myExpose = myConfigure = myMotion = myButtPress = myButtRelease = FALSE;
  192.  
  193.     openwindow(argv[0]);                  /* configure/open up the window */
  194.  
  195.     setupdevs(buttP, buttR, dialaxis);/* set up the dial and button boxes */
  196.  
  197.    /* 
  198.     * The event loop.
  199.     */
  200.     while (1) {         /* standard logic:  get event(s), process event(s) */
  201.  
  202.         XEvent event;
  203.         int axis_data[6];
  204.  
  205.         gflush();                        /* For proper DGL performance */
  206.  
  207.        /* this "do while" loop does the `get events' half of the "get events,
  208.         *  process events" action of the infinite while.  this is to ensure
  209.         *  the event queue is always drained before the events that have come
  210.         *  in are processed.
  211.         */
  212.         do {
  213.  
  214.             XNextEvent(dpy, &event);
  215.             switch (event.type) {
  216.  
  217.             /* "Expose" events are sort of like "REDRAW" in gl-speak in
  218.              *  terms of when a window becomes visible, or a previously
  219.              *  invisible part becomes visible.
  220.              */
  221.                 case Expose:                        
  222.                 /* see if either or both of the GL windows needs redrawing.
  223.          *  this way if only one of the two is generating the event,
  224.          *  only it will get repainted.
  225.          */
  226.                     for (i=1; i<MAXWINS; i++)
  227.                         if (event.xexpose.window == wins[i])
  228.                                 myExpose |= (1 << (i-1));
  229.                     break;
  230.  
  231.  
  232.             /* "ConfigNotify" events are like "REDRAW" in terms of changes
  233.              *   to a window's size or position.  Since this prog locks 
  234.              *   the window's size, ConfigureNotify will occur only when
  235.              *   the window gets moved.
  236.              */
  237.                 case ConfigureNotify:  
  238.                     myConfigure = TRUE;
  239.                     break;
  240.  
  241.  
  242.             /* "ButtonRelease" detects when LEFTMOUSE (Button1) is being
  243.              *  pressed indicating its time to leave.
  244.              */
  245.                 case ButtonRelease:
  246.                     if (event.xbutton.button == Button1)
  247.                         clean_exit();
  248.                     break;
  249.  
  250.  
  251.             /* "ClientMessage" is generated if the WM itself is being 
  252.              *  gunned down and sends an exit signal to any running prog.
  253.              */
  254.                 case ClientMessage:
  255.                     if (event.xclient.data.l[0] == del_atom)
  256.                         clean_exit();
  257.                     break;
  258.  
  259.  
  260.             /* "MapNotify" is generated when the window is made visible.
  261.              */
  262.                 case MapNotify:
  263.                     windowvisible = TRUE;
  264.                     break;
  265.  
  266.  
  267.             /* "UnmapNotify" is generated when the window is made "invisible"
  268.              *  --like when the window is iconified.  When the window is
  269.              *  not visible (i.e. iconified), we don't want any events to
  270.              *  be processed.
  271.              */
  272.                 case UnmapNotify:
  273.                     windowvisible = FALSE;
  274.                     break;
  275.  
  276.  
  277.             /* since interest is on the dNb's, they become the default.
  278.              */
  279.                 default:
  280.                 
  281.                     if (windowvisible) {    /* only process events if win is */
  282.                                           /* visible (not if it's iconified) */
  283.  
  284.                         if (event.type == DnB_motion_type) {  /* dial turned */
  285.  
  286.                             XDeviceMotionEvent *M=(XDeviceMotionEvent *)&event;
  287.  
  288.                             if (M->deviceid == DnB_device_id) {
  289.                                 i = M->first_axis;    /* save out which dial */
  290.                                 dialaxis[i]  = TRUE;  /* mark current "axis" */
  291.                                 dia[i].angle = M->axis_data[0];/* save angle */
  292.                                 myMotion = TRUE;          /* set motion flag */
  293.                             } else
  294.                                 fprintf(stderr,"cable has been disconnected\n");
  295.  
  296.                         } else if (event.type == DnB_press_type) {
  297.  
  298.                             XDeviceButtonEvent *B=(XDeviceButtonEvent *)&event;
  299.  
  300.                             if (B->deviceid == DnB_device_id) {
  301.                                 buttP[B->button-1] = TRUE;/* mark cur butpres*/
  302.                                 myButtPress = TRUE;      /* set butpres flag */
  303.                             } else
  304.                                 fprintf(stderr,"cable has been disconnected\n");
  305.                         
  306.                         } else if (event.type == DnB_release_type) {
  307.  
  308.                             XDeviceButtonEvent *B=(XDeviceButtonEvent *)&event;
  309.  
  310.                             if (B->deviceid == DnB_device_id) {
  311.                                 buttR[B->button-1] = TRUE; /* mark cur butrel*/
  312.                                 myButtRelease = TRUE;     /* set butrel flag */
  313.                             } else
  314.                                 fprintf(stderr,"cable has been disconnected\n");
  315.                         }
  316.                     }
  317.                     break;
  318.  
  319.             }  /* end switch (event.type) */
  320.  
  321.         } while (XPending(dpy));   /* end "do { } while".
  322.                                     * XPending() is like qtest()--it only 
  323.                                     * tells you if there're any events 
  324.                                     * presently in the queue.  it does not 
  325.                                     * disturb queue's contents in any way.
  326.                                     */
  327.  
  328.  
  329.     /* On an "Expose" event, redraw the popped, deiconified, or exposed-by-
  330.      *  another-window-being-pushed window.
  331.      */
  332.         if (myExpose) {
  333.             if (myExpose & 2) {
  334.                 Winset(wins[DBglwin], DBglwin); /* draw doublebufr'd window */
  335.                 makeDBwin();                  
  336.             }
  337.             if (myExpose & 1) {
  338.                 Winset(wins[SBglwin], SBglwin); /* draw singlebufr'd window */  
  339.                 makeSBwin();                   
  340.                 if (myButtPress) {             /* check to see if button is */
  341.                     for (i=0; i<NumButtons; i++) {   /* still being pressed */
  342.                         if (buttP[i])
  343.                             draw_button(i, SWITCH_ON);/* redraw pressed but */
  344.                     }
  345.                 }
  346.         }
  347.             myExpose = FALSE;               /* reset flag--queue now empty */
  348.         }
  349.  
  350.     /* On a "ConfigureNotify" event, the "GL" window has been moved. 
  351.      */
  352.         if (myConfigure) {
  353.             Winset(wins[DBglwin], DBglwin);  /* draw the DB win's contents */
  354.             viewport(0, (short)(XSIZE-YSIZE), 0, (short)(YSIZE-1));
  355.             makeDBwin();                   
  356.             Winset(wins[SBglwin], SBglwin);  /* draw the SB win's contents */
  357.             viewport(0, (short)(YSIZE-1), 0, (short)(YSIZE-1));
  358.             makeSBwin();                   
  359.             if (myButtPress) {                /* check to see if button is */
  360.                 for (i=0; i<NumButtons; i++) {      /* still being pressed */
  361.                     if (buttP[i]) 
  362.                         draw_button(i, SWITCH_ON);  /* redraw pressed butt */
  363.                 }
  364.             }
  365.             myConfigure = FALSE;            /* reset flag--queue now empty */
  366.         }
  367.  
  368.     /* For a motion-type event (a dial turning), update the angle values
  369.      * of whichever dials have just be sent to the event queue.
  370.      */
  371.         if (myMotion) {
  372.             Winset(wins[DBglwin], DBglwin);
  373.             for (i=0; i<NumDials; i++) {
  374.                 firstdialtouched = TRUE;         /* at this point a dial is */
  375.                 draw_dial(i);                    /* being turned.           */
  376.                 dialaxis[i] = FALSE;       
  377.             }
  378.             swapbuffers();
  379.             myMotion = FALSE;
  380.         }
  381.  
  382.     /* For a "button press"-type event draw whichever buttons have been
  383.      * pressed since the last time the event queue was drained.
  384.      */
  385.         if (myButtPress) {
  386.             Winset(wins[SBglwin], SBglwin);
  387.             for (i=0; i<NumButtons; i++) {
  388.                 if (buttP[i])
  389.                     draw_button(i, SWITCH_ON); /* draw button being pressed */
  390.             }
  391.         }
  392.  
  393.     /* For a "button release"-type event draw whichever buttons (in "off"
  394.      * position) have been released since last time the event q was drained.
  395.      */
  396.         if (myButtRelease) {
  397.             Winset(wins[SBglwin], SBglwin);
  398.             for (i=0; i<NumButtons; i++) {
  399.                 if (buttR[i])  {
  400.                     draw_button(i, SWITCH_OFF);
  401.                     buttR[i] = FALSE;
  402.                     buttP[i] = FALSE;
  403.                 }
  404.         }
  405.             myButtPress = FALSE;
  406.             myButtRelease = FALSE;
  407.         }
  408.     }
  409. }
  410.  
  411.  
  412.  
  413. #include <Xm/MwmUtil.h>
  414.  
  415.  
  416. /*  openwindow -
  417.  *     establish connection to X server, get screen info, specify the
  418.  *     attributes we want the WM to try to provide, and create the GL windows
  419.  */
  420. static void 
  421. openwindow(char *progname) {
  422.  
  423.     int screen_num;                            /* X screen number            */
  424.     long xorig, yorig;                         /* window (upper-left) origin */
  425.     XSizeHints Winhints;                          /* used to fix window size */
  426.     Atom atomName;
  427.     MotifWmHints mwm;
  428.  
  429.  
  430.  
  431.    /* connect to the X server and get screen info */
  432.     if ((dpy = XOpenDisplay(NULL)) == NULL) {
  433.         fprintf(stderr, "%s: cannot connect to X server %s\n",
  434.                                  progname, XDisplayName(NULL));
  435.         exit(1);
  436.     }
  437.     screen_num = DefaultScreen(dpy);
  438.  
  439.    /* define window (upper-left) origin coords */
  440.     xorig = (DisplayWidth(dpy, screen_num) - XSIZE) / 2;
  441.     yorig = (DisplayHeight(dpy, screen_num) - YSIZE) / 2;
  442.  
  443.    /* create top level X window which will be the parent of the 2 GL windows */
  444.     wins[top] = XCreateSimpleWindow(dpy, RootWindow(dpy, screen_num),
  445.                                     xorig, yorig, XSIZE, YSIZE, 0, 0, 0);
  446.  
  447.    /* specify the values for the Window Size Hints we want to enforce:  this 
  448.     *  window can be moved but we don't want to allow any resizing to occur. 
  449.     */
  450.     Winhints.x = xorig;         /* specify desired upper-left corner origin */
  451.     Winhints.y = yorig;         /* of window so prog will place itself      */
  452.     Winhints.width  = XSIZE;          /* specify desired x/y size of window */
  453.     Winhints.height = YSIZE;
  454.     Winhints.min_width = XSIZE;               /* set min and max width and  */
  455.     Winhints.max_width = XSIZE;               /* height to be the same so   */
  456.     Winhints.min_height = YSIZE;              /* window cannot be resized   */
  457.     Winhints.max_height = YSIZE;
  458.     Winhints.flags = USPosition|USSize|PMaxSize|PMinSize; /* set pert. flgs */
  459.     XSetNormalHints(dpy, wins[top], &Winhints);
  460.  
  461.    /* the following, culminating with XChangeProperty, enables us to remove
  462.     *  the "[re]size" menu item from the default window menu, the "maximize"
  463.     *  control button on the window frame, and the resize handle cursors 
  464.     *  that are normally available when the cursor moves into the proximity 
  465.     *  of any of the window's 4 corners.
  466.     */
  467.     mwm.flags = MWM_HINTS_FUNCTIONS;
  468.    /* remove the decorations from the function list we don't want to have */
  469.     mwm.functions = MWM_FUNC_ALL | MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE;
  470.     mwm.decorations = 0;
  471.     mwm.input_mode = 0;
  472.     atomName = XInternAtom(dpy, _XA_MOTIF_WM_HINTS, False);
  473.     XChangeProperty(dpy, wins[top], atomName, atomName, sizeof(long)*8, 
  474.             PropModeReplace, (unsigned char*) &mwm, sizeof(mwm)/sizeof(long));
  475.  
  476.    /* create the single-buffered (buttons/text) GL imaging window */
  477.     if ((wins[SBglwin] = glx_create_window(wins[top], 0, 0, 
  478.                                            YSIZE, YSIZE, 
  479.                                            SBglwin, progname)) == NULL) {
  480.         fprintf(stderr,"%s:  hdw doesn't support that window type\n",progname);
  481.         exit(1);
  482.     }
  483.  
  484.    /* create the double-buffered (dials) GL imaging window */
  485.     if ((wins[DBglwin] = glx_create_window(wins[top], YSIZE, 0, 
  486.                                            (XSIZE-YSIZE), YSIZE, 
  487.                                            DBglwin, progname)) == NULL) {
  488.         fprintf(stderr,"%s:  hdw doesn't support that window type\n",progname);
  489.         exit(1);
  490.     }
  491.  
  492.    /* define string that will show up in the window title bar (and icon) */
  493.     XStoreName(dpy, wins[top], "dial & button box confidence test");
  494.  
  495.    /* express interest in events we want each window to process */
  496.     XSelectInput(dpy, wins[top], StructureNotifyMask);
  497.     XSelectInput(dpy, wins[SBglwin], StructureNotifyMask | ButtonPressMask |
  498.                                      ButtonReleaseMask | ExposureMask);
  499.     XSelectInput(dpy, wins[DBglwin], StructureNotifyMask | ButtonPressMask |
  500.                                      ButtonReleaseMask | ExposureMask);
  501.  
  502.    /* express interest in WM killing this app */
  503.     if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", True)) != None)
  504.         XSetWMProtocols(dpy, wins[top], &del_atom, 1);
  505.  
  506.    /* map the windows--they won't actually be made visible until we process
  507.     *  the expose events up in main's infinite loop which these calls will 
  508.     *  generate.
  509.     */
  510.     XMapWindow(dpy, wins[DBglwin]);
  511.     XMapWindow(dpy, wins[SBglwin]);
  512.  
  513.    /* ensure the GL colormap is installed for this app */
  514.     XSetWMColormapWindows(dpy, wins[top], wins, MAXWINS);
  515.  
  516.    /* and now map the X parent */
  517.     XMapWindow(dpy, wins[top]);
  518. }
  519.  
  520.  
  521.  
  522. /* the next 124 lines contain the code used to create the GL rendering
  523.  *  windows, which will be the children of the X parent window created 
  524.  *  at the beginning of openwindow.
  525.  */
  526.  
  527. char *typeToName[] = {
  528.     "Colormap single buffer",
  529.     "Colormap double buffer",
  530. };
  531.  
  532.  
  533. /* Dorky little helper function used to build up a GLXconfig array.
  534.  */
  535. static void set_entry (GLXconfig* ptr, int b, int m, int a)
  536. {
  537.     ptr->buffer = b;
  538.     ptr->mode = m;
  539.     ptr->arg = a;
  540. }
  541.  
  542.  
  543. /*  glx_create_window -- Create a singlebuffered, colorindex X window 
  544.  *                       suitable for GL imaging.
  545.  *
  546.  * Parameters:   parent      - parent window for the GL imaging window
  547.  *               x, y        - window origin relative to parent
  548.  *               w, h        - window width and height
  549.  *               type        - single or double -buffer specifier
  550.  *               progname    - name of executable
  551.  */
  552. static Window 
  553. glx_create_window(Window parent, int x, int y, int w, int h, 
  554.                   int type, char *progname)
  555. {
  556.     GLXconfig params[50];
  557.     GLXconfig *next, *retconfig;
  558.     Colormap cmap = DefaultColormap(dpy, DefaultScreen(dpy));
  559.     XVisualInfo* vis;
  560.     XVisualInfo template;
  561.     XSetWindowAttributes cwa;
  562.     XWindowAttributes pwa;
  563.     int i, nret;
  564.     Window win;
  565.  
  566.  
  567.    /* first build an array in "params" that describes for GLXgetconfig(3G)
  568.     *  the type of GL drawing that will be done.
  569.     */
  570.     next = params;
  571.     switch (type) {
  572.         case SBglwin:
  573.             set_entry(next++, GLX_NORMAL, GLX_DOUBLE, FALSE);
  574.             break;
  575.         case DBglwin:
  576.             set_entry(next++, GLX_NORMAL, GLX_DOUBLE, TRUE);
  577.             break;
  578.     }
  579.     set_entry(next, 0, 0, 0);/* The input to GLXgetconfig is null terminated */
  580.  
  581.    /* Get configuration data for a window based on above parameters.
  582.     *  First we have to find out which screen the parent window is on,
  583.     *  then we can call GXLgetconfig()
  584.     */
  585.     XGetWindowAttributes(dpy, parent, &pwa);
  586.     retconfig = GLXgetconfig(dpy, XScreenNumberOfScreen(pwa.screen), params);
  587.     if (retconfig == 0) {
  588.         printf("Sorry, can't support %s type of windows\n", typeToName[type-1]);
  589.         exit(-1);
  590.     }
  591.     /* The GL sets its own X error handlers, which aren't as informative
  592.      *  when errors happen.  Calling XSetErrorHandler(0) here will
  593.      *  reset back to the default Xlib version.
  594.      */
  595.     XSetErrorHandler(0);
  596.  
  597.    /* Scan through config info, pulling info we need to create a window
  598.     *  that supports the rendering mode.
  599.     */
  600.     for (next = retconfig; next->buffer; next++) {
  601.         unsigned long buffer = next->buffer;
  602.         unsigned long mode = next->mode;
  603.         unsigned long value = next->arg;
  604.         switch (mode) {
  605.             case GLX_COLORMAP:
  606.                 if (buffer == GLX_NORMAL) {
  607.                     cmap = value;
  608.                 }
  609.                 break;
  610.             case GLX_VISUAL:
  611.                 if (buffer == GLX_NORMAL) {
  612.                     template.visualid = value;
  613.                     template.screen = DefaultScreen(dpy);
  614.                     vis = XGetVisualInfo(dpy, VisualScreenMask|VisualIDMask,
  615.                                           &template, &nret);
  616.                 }
  617.                 break;
  618.         }
  619.     }
  620.  
  621.    /* Create the window */
  622.     cwa.colormap = cmap;
  623.     cwa.border_pixel = 0;
  624.     win = XCreateWindow(dpy,parent,x,y,w,h,0,vis->depth,
  625.                         InputOutput,vis->visual,CWColormap|CWBorderPixel,&cwa);
  626.  
  627.    /* now rescan configuration info, find the window slot GLXgetconfig 
  628.     *  provided and fill it in with the window we just created.
  629.     */
  630.     for (next = retconfig; next->buffer; next++) {
  631.         if ((next->buffer == GLX_NORMAL) && (next->mode == GLX_WINDOW)) {
  632.             next->arg = win;
  633.             break;
  634.         }
  635.     }
  636.  
  637.    /* link to the GL */
  638.     if ((i = GLXlink(dpy, retconfig)) < 0) {
  639.         fprintf(stderr, "GLXlink returned %d for the %s window\n", 
  640.                                            i,   typeToName[type-1]);
  641.         exit(-1);
  642.     }
  643.  
  644.     return win;
  645. }
  646.  
  647.  
  648.  
  649. /*  setupdevs - 
  650.  *
  651.  *   establish a live connection to the dial+buttons device.
  652.  *
  653.  *   leverages off the "X11 Input Extension Library Specification" 
  654.  *   document (you *shud* be able to locate the on-line public access 
  655.  *   directory which contains all the files to print hard-copy of this
  656.  *   document under .../mit/doc/extensions/xinput).  refer to 
  657.  *   /usr/include/X11/extensions/{XI.h, XInput.h} for structures accessed.
  658.  */
  659. static void 
  660. setupdevs(int buttP[NumButtons], int buttR[NumButtons], int dialaxis[NumDials])
  661. {
  662.  
  663.     int i, ndevices;
  664.     XDevice *DnB_device;
  665.     XDeviceInfoPtr lp, list;
  666.     int num_ext_event_classes;
  667.     XEventClass ListOfEventClass[3];
  668.     int DnB_press_class, DnB_release_class, DnB_motion_class;
  669.  
  670.  
  671.     /* get a ptr to the list of all currently defined input devices */
  672.     list = (XDeviceInfoPtr) XListInputDevices(dpy, &ndevices); 
  673.     if (!list) {
  674.         fprintf(stderr,"XlistInputDevices failed to generate a devices list\n");
  675.         exit(1);
  676.     }
  677.  
  678.    /* check out the /usr/people/4Dgifts/examples/devices/input/X/Xlist.c
  679.     *  program (which gets compiled into "xlist").  running it will list
  680.     *  all the currently available input devices on the machine xlist is
  681.     *  run on.  there is a LOT that should be studied in the "input" subtree.
  682.     */
  683.     for (lp=list, i=0; i<ndevices; lp++, i++) {
  684.         if (lp->use==IsXExtensionDevice && strcmp(lp->name,"dial+buttons")==0) {
  685.             break;  /* found the right one--now save the ptr (lp) to it */
  686.         }
  687.     }
  688.     if (i == ndevices) {
  689.         fprintf(stderr, "\"dial+buttons\" device was not found\n");
  690.         exit(1);
  691.     }
  692.     DnB_device = XOpenDevice(dpy, lp->id);           /* open the DnB device */
  693.     if (!DnB_device) {
  694.         fprintf(stderr, "XOpenDevice failed for \"dial+buttons\" device\n");
  695.         exit(1);
  696.     }
  697.     DnB_device_id = DnB_device->device_id;        /* save out the device id */
  698.  
  699.    /* the following 3 macros determine the given event's type and class.  
  700.     *  each macro is passed the structure that describes the device from 
  701.     *  which input is desired.
  702.     */
  703.     DeviceButtonPress(DnB_device, DnB_press_type, DnB_press_class);
  704.     DeviceButtonRelease(DnB_device, DnB_release_type, DnB_release_class);
  705.     DeviceMotionNotify(DnB_device, DnB_motion_type, DnB_motion_class);
  706.  
  707.     ListOfEventClass[0] = DnB_press_class;
  708.     ListOfEventClass[1] = DnB_release_class;
  709.     ListOfEventClass[2] = DnB_motion_class;
  710.  
  711.     num_ext_event_classes = 3;
  712.  
  713.    /* XSelectExtensionEvent requests the server to send events that match
  714.     *  the events and devices described by the event list and that come
  715.     *  from the requested window.
  716.     * first, we'll put in our button press/release requests for the
  717.     *  single-buffered (buttons and text) GL window.
  718.     */
  719.     XSelectExtensionEvent(dpy, wins[SBglwin],
  720.                           ListOfEventClass, num_ext_event_classes);
  721.  
  722.    /* now we'll request the Motion events for the double-buffered (dials)
  723.     *  GL window.  
  724.     * note that in each of these two invocations of XSelectExtensionEvent,
  725.     *  we're selecting all three event classes in each window.  this is 
  726.     *  because, no matter where the cursor is inside the window containing 
  727.     *  both the single and double -buffered GL windows, we want to process 
  728.     *  dial or button events, whether or not the mouse is physically 
  729.     *  present in either of these two respective windows.
  730.     */
  731.     XSelectExtensionEvent(dpy, wins[DBglwin],
  732.                           ListOfEventClass, num_ext_event_classes);
  733.  
  734.    /* initialize all the buttons and dial "markers" to indicate none have 
  735.     *  yet been pressed or moved.
  736.     */
  737.     for (i=0; i<NumButtons; i++) {
  738.         buttP[i]=FALSE;
  739.         buttR[i]=FALSE;
  740.     }
  741.     for (i=0; i<NumDials; i++) {
  742.         dialaxis[i]=FALSE;
  743.     }
  744. }
  745.  
  746.  
  747.  
  748. /* A little helper wrapper for GLXwinset.
  749.  *  It passes the global variable "dpy" which contains the display, and it
  750.  *  checks the return value.  This makes the call to begin GL drawing a
  751.  *  little simpler.  Building in such automatic error checking is always a
  752.  *  "smooth move" (*not* like the cancerously-mutant human with the big
  753.  *  proboscis who is plastered all over the place urging people to be
  754.  *  likewise smoothly cancerous and hardly cool...).
  755.  */
  756. static void Winset(Window w, const int type)
  757. {
  758.     int rv = GLXwinset(dpy, w);
  759.     if (rv < 0) {
  760.         fprintf(stderr, "GLXWinset returned %d\ for the %s window\n",
  761.                                             rv,   typeToName[type-1]);
  762.         exit(-1);
  763.     }
  764. }
  765.  
  766.  
  767.  
  768. /*  makeSBwin -- Draw the buttons' and text's singlebuffer'd GL window
  769.  */
  770. static void 
  771. makeSBwin()
  772. {
  773.     int  i;
  774.  
  775.  
  776.     if (!firstdialtouched)
  777.         ortho2(0.0, 40.0, 0.0, 40.0);
  778.     
  779.     color(WHITE);
  780.     clear();
  781.  
  782.     for (i=0; i<NumButtons; i++)
  783.         draw_button(i, SWITCH_OFF);                  /* draw the buttons */
  784.  
  785.     color(BLACK);
  786.     cmov2i(2,1);
  787.     charstr("Use left mouse button to quit.");
  788.  
  789. }
  790.  
  791.  
  792.  
  793. /*  makeDBwin -- Draw the dials' doublebuffer'd GL window
  794.  */
  795. static void 
  796. makeDBwin()
  797. {
  798.     int  i;
  799.     
  800.  
  801.     if (myExpose)                 /*  if we're coming thru the first time */
  802.         frontbuffer(TRUE);         /* write into (initialize) both buffers */
  803.  
  804.     if (!firstdialtouched)
  805.         ortho2(0.0, 30.0, 0.0, 40.0);
  806.     
  807.     color(WHITE);
  808.     clear();
  809.  
  810.     for (i=0; i<NumDials; i++)
  811.         draw_dial(i);                              /* and draw the dials */
  812.  
  813.     swapbuffers();
  814.  
  815.     if (myExpose)
  816.         frontbuffer(FALSE);
  817. }
  818.  
  819.  
  820.  
  821.  
  822.  
  823.  
  824. /*  draw_dial --  Draw dial number dialno ([0..7])
  825.  */
  826. static void 
  827. draw_dial(int dialnum)
  828. {
  829.     DB_DIAL *dptr = &dia[dialnum];
  830.     Coord   xcenter, ycenter;
  831.     long    angle;
  832.     char    str[80];
  833.  
  834.  
  835.     xcenter = dptr->xcntr;
  836.     ycenter = dptr->ycntr;
  837.     angle   = dptr->angle;
  838.  
  839.     if ((firstdialtouched) &&             /* when we know a dial has        */
  840.         (dialaxis[dialnum])) {            /* actually been turned, update   */
  841.         Winset(wins[SBglwin], SBglwin);   /* the text area and the old-     */
  842.         color(WHITE);                     /* sytle buttons box text-display */
  843.         rectf(2,2,24,5);                  
  844.         sprintf(str,"Dial %d intensity is %d",dialnum,angle);
  845.         cmov2i(2,3);
  846.         color(BLUE);
  847.         charstr(str);
  848.         Winset(wins[DBglwin], DBglwin);
  849.         sprintf(str, "DIAL %d", dialnum);
  850.         dbtext(str);
  851.     }
  852.  
  853.     angle   = (dptr->angle * 3600) >> 10;
  854.     color(WHITE);
  855.     circf(xcenter, ycenter, 2.5);
  856.     color(BLACK);
  857.     circ(xcenter, ycenter, 2.5);
  858.     color(RED);
  859.     arcf(xcenter, ycenter, 2.5, 900-angle, 900);
  860. }
  861.  
  862.  
  863.  
  864. #define BSIZE           3.0
  865.  
  866. /*  draw_button --  Draw button number buttno in its on or off state
  867.  */
  868. static void 
  869. draw_button(int buttno, int state)
  870. {
  871.     DB_BUTTON      *bptr = &but[buttno];
  872.     unsigned long  button_led = 0;
  873.     Coord          xpos, ypos;
  874.     char           str[80];
  875.     
  876.  
  877.     xpos = bptr->xpos;
  878.     ypos = bptr->ypos;
  879.  
  880.     if (state == SWITCH_ON) {
  881.         color(WHITE);                 /* make sure the correct buttons  */
  882.         rectf(2,2,24,5);              /* box led (old models) lights up */
  883.         button_led |= (1 << buttno);
  884.         setdblights(button_led);                
  885.  
  886.         cmov2i(2,3);                  /* update the text string in the  */
  887.         color(BLUE);                  /* bottom-left corner of the win  */
  888.         sprintf(str, "Button %d is being pressed", buttno);
  889.         charstr(str);
  890.  
  891.         sprintf(str, "BUT %d", buttno);  /* update the buttons box (old */
  892.         dbtext(str);                     /* models) text display        */
  893.  
  894.         color(RED);                      /* draw the current button red */
  895.         rectf(xpos, ypos, xpos+BSIZE, ypos+BSIZE);
  896.  
  897.     } else {
  898.         button_led &= ~(1 << buttno);
  899.         setdblights(button_led);
  900.         color(WHITE);
  901.         rectf(2,2,24,5);
  902.         rectf(xpos, ypos, xpos+BSIZE, ypos+BSIZE);
  903.         color(BLACK);
  904.         rect(xpos, ypos, xpos+BSIZE, ypos+BSIZE);
  905.     }
  906.  
  907. }
  908.  
  909.  
  910.  
  911. /*  clean_exit  --  Clean up before exiting
  912.  */
  913. static void 
  914. clean_exit(void)
  915. {
  916.     XCloseDisplay(dpy);
  917.     exit(0);
  918. }
  919.